package com.neusoft.neuiotms.common.service.impl;

import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.locks.ReentrantLock;

import javax.persistence.EntityNotFoundException;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CachePut;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;

import com.neusoft.bizcore.web.constant.ExceptionConstants;
import com.neusoft.bizcore.web.exception.UnitedException;
import com.neusoft.bizcore.web.support.Searchable;
import com.neusoft.neuiotms.common.converter.SerialNumberConverter;
import com.neusoft.neuiotms.common.dto.SerialNumberDTO;
import com.neusoft.neuiotms.common.model.SerialNumber;
import com.neusoft.neuiotms.common.repository.SerialNumberRepository;
import com.neusoft.neuiotms.common.service.SerialNumberService;

@Service
public class SerialNumberServiceImpl implements SerialNumberService {

    @Autowired
    private SerialNumberRepository serialNumberRepository;

    @Autowired
    private SerialNumberConverter serialNumberConverter;

    /** 生成器锁 */
    private final ReentrantLock lock = new ReentrantLock();

    /** 流水号格式化器 */
    private DecimalFormat format = null;

    /** 预生成锁 */
    private final ReentrantLock prepareLock = new ReentrantLock();

    /** 最小值 */
    private final int min = 0;

    /** 最大值 */
    private int max = 0;

    /** 预生成数量 */
    private int prepare = 0;

    /** 数据库存储的当前最大序列号 **/
    int maxSerialInt = 0;

    SerialNumberDTO serialNumberDTO = new SerialNumberDTO();

    /** 预生成流水号 */
    Map<String, List<String>> prepareSerialNumberMap = new HashMap<>();

    /**
     * 根据模块code生成序列号
     *
     * @param moduleCode 模块code
     * @return 序列号
     */
    @Override
    public String generateSerialNumberByModelCode(final String moduleCode) {
        //预序列号加锁
        this.prepareLock.lock();
        try {
            //判断内存中是否还有序列号
            if ((null != this.prepareSerialNumberMap.get(moduleCode))
                    && !this.prepareSerialNumberMap.get(moduleCode).isEmpty()) {
                //若有，返回第一个，并删除
                return this.prepareSerialNumberMap.get(moduleCode).remove(0);
            }
        } finally {
            //预序列号解锁
            this.prepareLock.unlock();
        }
        this.serialNumberDTO = new SerialNumberDTO();
        final SerialNumber serialNumber = this.serialNumberRepository.findByModuleCode(moduleCode);
        this.serialNumberDTO = this.serialNumberConverter.toDTO(serialNumber);

        this.prepare = this.serialNumberDTO.getPreMaxNum(); //预生成流水号数量
        String pattern = this.serialNumberDTO.getConfigTemplet().trim(); //配置模板
        final Boolean autoIncrement = this.serialNumberDTO.getAutoIncrement();
        this.maxSerialInt = this.serialNumberDTO.getMaxSerial(); //数据库存储的最大序列号
        this.max = this.counter(pattern, '0') + 1;//根据模板判断当前序列号数字的最大值
        if (autoIncrement) {
            pattern = pattern.replace("0", "#");
        }
        this.format = new DecimalFormat(pattern);
        //生成预序列号，存到缓存中
        final List<String> resultList = this.generatePrepareSerialNumbers(moduleCode);
        this.prepareLock.lock();
        try {
            this.prepareSerialNumberMap.put(moduleCode, resultList);
            return this.prepareSerialNumberMap.get(moduleCode).remove(0);
        } finally {
            this.prepareLock.unlock();
        }
    }

    @Override
    public SerialNumberDTO details(final Long id) {
        final SerialNumber model;
        try {
            model = this.serialNumberRepository.getOne(id);
        } catch (final EntityNotFoundException e) {
            throw new UnitedException(ExceptionConstants.ENTITY_NOT_FOUND, new Object[] {id });
        }

        return this.serialNumberConverter.toDTO(model);
    }

    @Override
    public SerialNumberDTO update(final Long id, final SerialNumberDTO dto) {
        SerialNumber model;
        try {
            model = this.serialNumberRepository.getOne(id);
        } catch (final EntityNotFoundException e) {
            throw new UnitedException(ExceptionConstants.ENTITY_NOT_FOUND, new Object[] {id });
        }

        dto.setId(model.getId());
        model = this.serialNumberConverter.toModel(dto);
        this.serialNumberRepository.save(model);

        return this.serialNumberConverter.toDTO(model);
    }

    @Override
    public SerialNumberDTO create(final SerialNumberDTO dto) {
        final SerialNumber model = this.serialNumberConverter.toModel(dto);
        this.serialNumberRepository.save(model);
        return this.serialNumberConverter.toDTO(model);
    }

    @Override
    public void delete(final Long id) {
        try {
            this.serialNumberRepository.deleteById(id);
        } catch (final EmptyResultDataAccessException e) {
            throw new UnitedException(ExceptionConstants.ENTITY_NOT_FOUND, new Object[] {id });
        }
    }

    @Override
    public List<SerialNumberDTO> findAll() {
        final List<SerialNumber> models = this.serialNumberRepository.findAll();
        return this.serialNumberConverter.toListDTO(models);
    }

    @Override
    public Page<SerialNumberDTO> search(final Pageable pageable, final Searchable searchable) {
        final Page<SerialNumber> result = this.serialNumberRepository.search(pageable, searchable);
        return this.serialNumberConverter.toPageDTO(result);
    }

    /**
     * 根据模块code生成预数量的序列号存放到Map中
     *
     * @param moduleCode 模块code
     * @return
     */
    @CachePut(value = "serialNumber", key = "#moduleCode")
    private List<String> generatePrepareSerialNumbers(final String moduleCode) {
        //临时List变量
        final List<String> resultList = new ArrayList<>(this.prepare);
        this.lock.lock();
        try {
            for (int i = 0; i < this.prepare; i++) {
                /** 已生成流水号（种子） */
                Integer seed;
                this.maxSerialInt = this.maxSerialInt + 1;
                if ((this.maxSerialInt > this.min) && ((this.maxSerialInt + "").length() < this.max)) {
                    seed = this.maxSerialInt;
                } else {
                    //如果动态数字长度大于模板中的长度 例：模板CF000  maxSerialInt 1000
                    seed = this.maxSerialInt = 0;
                    //更新数据，重置maxSerialInt为0
                    this.serialNumberDTO.setMaxSerial(0);
                    this.update(this.serialNumberDTO.getId(), this.serialNumberDTO);
                }
                //动态数字生成
                final String formatSerialNum = this.format.format(seed);
                resultList.add(formatSerialNum);
            }
            //更新数据
            this.serialNumberDTO.setMaxSerial(this.maxSerialInt);
            this.update(this.serialNumberDTO.getId(), this.serialNumberDTO);
        } finally {
            this.lock.unlock();
        }
        return resultList;
    }

    /**
     * 统计某一个字符出现的次数
     *
     * @param str 查找的字符
     * @param c
     * @return
     */
    private int counter(final String str, final char c) {
        int count = 0;
        for (int i = 0; i < str.length(); i++) {
            if (str.charAt(i) == c) {
                count++;
            }
        }
        return count;
    }

}
